Return to start page
Systems/Character/Struct Talk.j
1 library AStructSystemsCharacterTalk requires optional ALibraryCoreDebugMisc, AStructCoreGeneralHashTable, AStructCoreGeneralVector, ALibraryCoreInterfaceMisc, ALibraryCoreMathsHandle, AStructSystemsCharacterCharacter
2
3 /// @todo Should be a part of @struct ATalk, vJass bug.
4 function interface ATalkStartAction takes ATalk talk returns nothing
5
6 /// @todo Should be a static method of @struct ATalk, vJass bug.
7 private function AInfoActionBackToStartPage takes AInfo info returns nothing
8 call info.talk().showStartPage()
9 endfunction
10
11 /// @todo Should be a static method of @struct ATalk, vJass bug.
12 private function AInfoActionExit takes AInfo info returns nothing
13 call info.talk().close()
14 endfunction
15
16 /**
17 * Talks are a kind of dialogs with NPCs which are implemented by using the Warcraft 3 dialog natives.
18 * This means that choices in the form of dialog buttons are shown to a character owner.
19 * If the owner presses any button an user-defined function will be called where the user can define the whole talk.
20 * One talk contains one or several infos (@struct AInfo). These infos contain the user-defined function and an user-defined condition.
21 * Besides they can have various other properties.
22 * Note that only one character owner can use one talk at the same time. There is no support for several character owners talking to the same NPC, yet.
23 */
24 struct ATalk
25 public static constant integer maxInfos = 100
26 //static start members
27 private static string order
28 private static real maxDistance
29 private static string effectPath
30 private static string textErrorMessage
31 private static string textExit
32 private static string textBack
33 //start members
34 private unit m_unit
35 private ATalkStartAction m_startAction
36 //members
37 private AIntegerVector m_infos
38 private ACharacter m_character
39 private boolean m_isEnabled
40 private trigger m_orderTrigger
41 private effect m_effect
42
43 //! runtextmacro optional A_STRUCT_DEBUG("\"ATalk\"")
44
45 //start members
46
47 /// @return Returns the NPC's unit.
48 public method unit takes nothing returns unit
49 return this.m_unit
50 endmethod
51
52 public method startAction takes nothing returns ATalkStartAction
53 return this.m_startAction
54 endmethod
55
56 //members
57
58 /// @return Returns the character which is talking currently to the NPC.
59 public method character takes nothing returns ACharacter
60 return this.m_character
61 endmethod
62
63 public method isEnabled takes nothing returns boolean
64 return this.m_isEnabled
65 endmethod
66
67 //convenience methods
68
69 /**
70 * Shows the talk's Warcraft 3 dialog.
71 * If no info has been added start page is shown automatically.
72 */
73 public method show takes nothing returns nothing
74 if (AGui.playerGui(this.m_character.user()).dialog().dialogButtons() == 0) then
75 call this.showStartPage()
76 else
77 call AGui.playerGui(this.m_character.user()).dialog().show()
78 endif
79 endmethod
80
81 public method showAll takes nothing returns nothing
82 local integer i = 0
83 loop
84 exitwhen (i == this.m_infos.size())
85 call AInfo(this.m_infos[i]).show()
86 set i = i + 1
87 endloop
88 call this.show()
89 endmethod
90
91 public method hide takes nothing returns nothing
92 call AGui.playerGui(this.m_character.user()).dialog().hide()
93 endmethod
94
95 /// You do not have to clear the dialog in the start action!
96 public method clear takes nothing returns nothing
97 local integer i = 0
98 loop
99 exitwhen (i == this.m_infos.size())
100 call AInfo(this.m_infos[i]).hide()
101 set i = i + 1
102 endloop
103 call AGui.playerGui(this.m_character.user()).dialog().clear()
104 endmethod
105
106 public method addInfo takes boolean permanent, boolean important, AInfoCondition condition, AInfoAction action, string description returns AInfo
107 return AInfo.create(this, permanent, important, condition, action, description)
108 endmethod
109
110 public method addBackButton takes AInfoAction action returns AInfo
111 return AInfo.create(this, true, false, 0, action, ATalk.textBack)
112 endmethod
113
114 public method addBackToStartPageButton takes nothing returns AInfo
115 return AInfo.create(this, true, false, 0, AInfoActionBackToStartPage, ATalk.textBack)
116 endmethod
117
118 public method addExitButton takes nothing returns AInfo
119 return AInfo.create(this, true, false, 0, AInfoActionExit, ATalk.textExit)
120 endmethod
121
122 public method showStartPage takes nothing returns nothing
123 call this.m_startAction.execute(this) //create buttons
124 endmethod
125
126 /// @returns Returns if info with index @param index has already been shown to the character which is talking currently to the NPC.
127 public method infoHasBeenShown takes integer index returns boolean
128 return AInfo(this.m_infos[index]).hasBeenShownToCharacter(this.m_character.userId())
129 endmethod
130
131 //methods
132
133 /// Usually you don't have to call this method since talks will be activated by a specific unit order.
134 public method openForCharacter takes ACharacter character returns nothing
135 debug if (this.m_character != 0) then
136 debug call this.print("Character is not 0.")
137 debug return
138 debug endif
139 set this.m_character = character
140 call SetUserInterfaceForPlayer(character.user(), false, true)
141 call character.setTalk(this)
142 call character.setMovable(false)
143 call PauseUnit(this.m_unit, true) //Disables routines or something else
144 call SetUnitFacing(character.unit(), GetAngleBetweenUnits(character.unit(), this.m_unit))
145 call SetUnitFacing(this.m_unit, GetAngleBetweenUnits(this.m_unit, character.unit()))
146 call SetUnitLookAt(character.unit(), "bone_head", this.m_unit, 0.0, 0.0, GetUnitFlyHeight(this.m_unit) + 90.0)
147 call SetUnitLookAt(this.m_unit, "bone_head", character.unit(), 0.0, 0.0, GetUnitFlyHeight(character.unit()) + 90.0)
148 call AThirdPersonCamera.playerThirdPersonCamera(character.user()).resetCamAoa()
149 call AThirdPersonCamera.playerThirdPersonCamera(character.user()).resetCamRot()
150 call AThirdPersonCamera.playerThirdPersonCamera(character.user()).disable()
151 call AThirdPersonCamera.playerThirdPersonCamera(character.user()).enable(character.unit(), 0.0)
152 call AGui.playerGui(character.user()).dialog().clear()
153 call AGui.playerGui(character.user()).dialog().setMessage(GetUnitName(this.m_unit))
154 //call this.clear()
155 call this.m_startAction.execute(this) //create buttons
156 endmethod
157
158 public method close takes nothing returns nothing
159 local player characterUser = this.m_character.user()
160 call AGui.playerGui(characterUser).dialog().clear()
161 call ResetUnitLookAt(this.m_character.unit())
162 call ResetUnitLookAt(this.m_unit)
163 call SetUserInterfaceForPlayer(characterUser, true, true)
164 if (not ACharacter.useViewSystem() or not this.m_character.view().enableAgain()) then
165 call AThirdPersonCamera.playerThirdPersonCamera(characterUser).pause()
166 call ResetToGameCameraForPlayer(characterUser, 0.0)
167 endif
168 call this.m_character.setTalk(0)
169 call this.m_character.setMovable(true)
170 set this.m_character = 0
171 call PauseUnit(this.m_unit, false) //Enables routines or something else
172 set characterUser = null
173 endmethod
174
175 public method enable takes nothing returns nothing
176 set this.m_isEnabled = true
177 call EnableTrigger(this.m_orderTrigger)
178 if (thistype.effectPath != null) then
179 set this.m_effect = AddSpecialEffectTarget(thistype.effectPath, this.m_unit, "overhead")
180 endif
181 endmethod
182
183 public method disable takes nothing returns nothing
184 set this.m_isEnabled = false
185 call DisableTrigger(this.m_orderTrigger)
186 if (thistype.effectPath != null) then
187 call DestroyEffect(this.m_effect)
188 set this.m_effect = null
189 endif
190 endmethod
191
192 /// Used by @struct AInfo.
193 public method showInfo takes integer index returns boolean
194 return AInfo(this.m_infos[index]).show()
195 endmethod
196
197 /// Used by @function ADialogButtonAction.
198 public method runInfo takes integer index returns nothing
199 call AInfo(this.m_infos[index]).run()
200 endmethod
201
202 public method getInfoByDialogButtonIndex takes integer dialogButtonIndex returns AInfo
203 local integer i = 0
204 loop
205 exitwhen (i == this.m_infos.size())
206 if (AInfo(this.m_infos[i]).isShown() and AInfo(this.m_infos[i]).dialogButtonIndex() == dialogButtonIndex) then
207 return AInfo(this.m_infos[i])
208 endif
209 set i = i + 1
210 endloop
211 return 0
212 endmethod
213
214 /// Friend relationship to @struct AInfo, do not use.
215 public method addInfoInstance takes AInfo info returns integer
216 call this.m_infos.pushBack(info)
217 return this.m_infos.backIndex()
218 endmethod
219
220 /// Friend relationship to @struct AInfo, do not use.
221 public method removeInfoInstanceByIndex takes integer index returns nothing
222 call this.m_infos.erase(index)
223 endmethod
224
225 private static method triggerConditionOpen takes nothing returns boolean
226 local trigger triggeringTrigger
227 local unit triggerUnit
228 local player owner
229 local unit orderTargetUnit
230 local thistype this
231 local boolean result = false
232 if (GetIssuedOrderId() == OrderId(ATalk.order)) then //Rechtsklick
233 set triggerUnit = GetTriggerUnit()
234 set owner = GetOwningPlayer(triggerUnit)
235 // Is character, if there is shared control or controller is computer player talks can not be used.
236 if (GetPlayerSlotState(owner) != PLAYER_SLOT_STATE_LEFT and GetPlayerController(owner) != MAP_CONTROL_COMPUTER and triggerUnit == ACharacter.playerCharacter(owner).unit()) then
237 set triggeringTrigger = GetTriggeringTrigger()
238 set this = AHashTable.global().handleInteger(triggeringTrigger, "this")
239 set orderTargetUnit = GetOrderTargetUnit()
240 if (orderTargetUnit == this.m_unit) then
241 if (GetDistanceBetweenUnits(triggerUnit, orderTargetUnit, 0.0, 0.0) <= ATalk.maxDistance) then //Z value is not checked
242 set result = (this.m_character == 0)
243 if (not result) then
244 call ACharacter.playerCharacter(owner).displayMessage(ACharacter.messageTypeError, ATalk.textErrorMessage)
245 endif
246 endif
247 endif
248 set triggeringTrigger = null
249 set orderTargetUnit = null
250 endif
251 set triggerUnit = null
252 set owner = null
253 endif
254 return result
255 endmethod
256
257 private static method triggerActionOpen takes nothing returns nothing
258 local trigger triggeringTrigger = GetTriggeringTrigger()
259 local player triggerPlayer = GetTriggerPlayer()
260 local thistype this = AHashTable.global().handleInteger(triggeringTrigger, "this")
261 call IssueImmediateOrder(ACharacter.playerCharacter(triggerPlayer).unit(), "stop")
262 call this.openForCharacter(ACharacter.playerCharacter(triggerPlayer))
263 set triggeringTrigger = null
264 set triggerPlayer = null
265 endmethod
266
267 private method createOrderTrigger takes nothing returns nothing
268 local conditionfunc conditionFunction
269 local triggercondition triggerCondition
270 local triggeraction triggerAction
271 set this.m_orderTrigger = CreateTrigger()
272 call TriggerRegisterAnyUnitEventBJ(this.m_orderTrigger, EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER)
273 set conditionFunction = Condition(function thistype.triggerConditionOpen)
274 set triggerCondition = TriggerAddCondition(this.m_orderTrigger, conditionFunction)
275 set triggerAction = TriggerAddAction(this.m_orderTrigger, function thistype.triggerActionOpen)
276 call AHashTable.global().setHandleInteger(this.m_orderTrigger, "this", this)
277 set conditionFunction = null
278 set triggerCondition = null
279 set triggerAction = null
280 endmethod
281
282 private method createEffect takes nothing returns nothing
283 if (thistype.effectPath != null) then
284 set this.m_effect = AddSpecialEffectTarget(thistype.effectPath, this.m_unit, "overhead")
285 endif
286 endmethod
287
288 public static method create takes unit usedUnit, ATalkStartAction startAction returns thistype
289 local thistype this = thistype.allocate()
290 //start members
291 set this.m_unit = usedUnit
292 set this.m_startAction = startAction
293 //members
294 set this.m_infos = AIntegerVector.create()
295 set this.m_character = 0
296 set this.m_isEnabled = true
297
298 call this.createOrderTrigger()
299 call this.createEffect()
300 return this
301 endmethod
302
303 private method destroyOrderTrigger takes nothing returns nothing
304 call AHashTable.global().destroyTrigger(this.m_orderTrigger)
305 set this.m_orderTrigger = null
306 endmethod
307
308 private method destroyEffect takes nothing returns nothing
309 if (thistype.effectPath != null) then
310 call DestroyEffect(this.m_effect)
311 set this.m_effect = null
312 endif
313 endmethod
314
315 private method destroyInfos takes nothing returns nothing
316 loop
317 exitwhen (this.m_infos.empty())
318 call AInfo(this.m_infos.back()).destroy()
319 endloop
320 call this.m_infos.destroy()
321 endmethod
322
323 public method onDestroy takes nothing returns nothing
324 //start members
325 set this.m_unit = null
326
327 call this.destroyInfos()
328 call this.destroyOrderTrigger()
329 call this.destroyEffect()
330 endmethod
331
332 public static method init takes string order, real maxDistance, string effectPath, string textErrorMessage, string textExit, string textBack returns nothing
333 //static start members
334 set thistype.order = order
335 set thistype.maxDistance = maxDistance
336 set thistype.effectPath = effectPath
337 set thistype.textErrorMessage = textErrorMessage
338 set thistype.textExit = textExit
339 set thistype.textBack = textBack
340 endmethod
341 endstruct
342
343 endlibrary